//
//  DemoOOP.swift
//  swiftDemo
//
//  Created by JIENING ZHANG on 2020/1/17.
//  Copyright © 2020 lovivid. All rights reserved.
//

// https://www.jianshu.com/p/ecc747ff8559

import UIKit
import Foundation

enum DemoMoneyKind:Int {
    
    case DemoMoneyKindRMB = 0 // 人民币
    case DemoMoneyKindUSD     // 美元
    case DemoMoneyKindHKD     // 港元
    case DemoMoneyKindJPY     // 日元
    case DemoMoneyKindEUR     // 欧元
    case DemoMoneyKindGBP     // 英镑
    
    // Swift 中，枚举类型是一等（first-class）类型。
    // 它们采用了很多在传统上只被类（class）所支持的特性，例如计算属性（computed properties），用于提供枚举值的附加信息，
    // 实例方法（instance methods），用于提供和枚举值相关联的功能。
    // 枚举也可以定义构造函数（initializers）来提供一个初始值；可以在原始实现的基础上扩展它们的功能；还可以遵循协议（protocols）来提供标准的功能。
    
    func getMoneyKindNames() -> (String, String) {
        switch self {
            case .DemoMoneyKindRMB:
                return ("RMB", "人民币")
            case .DemoMoneyKindUSD:
                return ("USD", "美元")
            case .DemoMoneyKindHKD:
                return ("HKD", "港元")
            case .DemoMoneyKindJPY:
                return ("JPY", "日元")
            case .DemoMoneyKindEUR:
                return ("EUR", "欧元")
            case .DemoMoneyKindGBP:
                return ("GBP", "英镑")
            default:
                return ("", "")
        }
    }
}

struct DemoCategory {

    var categoryID : String       // 类目id
    var categoryName : String     // 类目名称
    var parentID : String         // 类目父 id
    var cDate : Date              // 创建时间
}



class DemoAccount : CustomStringConvertible {
    
    private var accountID : Int = 0     // 账目id，使用创建时间的时间戳，精确到秒
    var amount : Float = 0              // 金额，单位元
    var comment : String = ""           // 备注
    var aDate : Date                    // 账目发生时间
    var mDate : Date                    // 最近修改时间
    var cateArr : [Int] = []            // 账目所属 类目id 字符串列表，在dic中，是以竖线 | 分割的类目id字符串
    var photoArr : [String] = []        // 账目关联的照片文件名列表，在dic中，是以竖线 | 分割的图片文件名字符串
    var latitude : Double = 0           // 账目发生，从设备拿到的坐标位置 纬度值
    var longitude : Double = 0          // 账目发生，从设备拿到的坐标位置 经度值
    
    var moneyKind : DemoMoneyKind = .DemoMoneyKindRMB   // 钱的类型, LZKMoneyKind 对象的 kindID
    
    var moneyKindTuple : (Int, String, String) {        // 钱的类型元组, 计算属性，(Int序号, 英文缩写, 中文名)
        
        // 只读计算属性的声明可以去掉 get 关键字和花括号
        // 如果整个 getter 是单一表达式，getter 会隐式地返回这个表达式结果
        (self.moneyKind.rawValue, self.moneyKind.getMoneyKindNames().0, self.moneyKind.getMoneyKindNames().1)
    }
    
    
    
    var cDate : Date {                                  // 账目创建时间计算属性，由accountID计算得出 (时间戳--》日期对象)
        
        // 计算属性不直接存储值，而是提供一个 getter 和一个可选的 setter，来间接获取和设置其他属性或变量的值。
        get {
           let accountDate = Date.init(timeIntervalSince1970:Double(self.accountID))
           return accountDate
        }
        
        set {
            // 如果计算属性的 setter 没有定义表示新值的参数名，则可以使用默认名称 newValue
            // 日期对象 --》时间戳
            self.accountID = Int(newValue.timeIntervalSince1970);
        }
    }
    
    init() {
        // 在此处执行构造过程
        let nowDate = Date();
        self.accountID = Int(nowDate.timeIntervalSince1970);
        self.aDate = nowDate
        self.mDate = nowDate
    }
    
    // 实现协议 CustomStringConvertible，如此这个类的对象在被打印成字符串的时候调用如下的方法
    var description : String {
        "Account : {accountID : \(self.accountID), cDate : \(self.cDate),  moneyKindTuple: \(self.moneyKindTuple)}"
    }
}

func createPointerFrom<T>(_ elem: UnsafePointer<T>) -> UnsafePointer<T> {
    return elem
}

class DemoOOP: NSObject {
    class func demoLogic() {
        // struct 逐一成员构造器, class 没有逐一成员构造器
        var testCategory = DemoCategory(categoryID:"1", categoryName:"日常消费", parentID:"0", cDate: Date())
        
        // struct 内存地址
        var structInstanceAdress = createPointerFrom(&testCategory);
        print("\(structInstanceAdress)  -->  \(testCategory)")
        
        // Swift 中所有的基本类型：整数（integer）、浮点数（floating-point number）、布尔值（boolean）、字符串（string)、数组（array）和字典（dictionary），都是值类型，其底层也是使用结构体实现的。
        // Swift 中所有的结构体和枚举类型都是值类型。这意味着它们的实例，以及实例中所包含的任何值类型的属性，在代码中传递的时候都会被复制。
        var testCategory2 = testCategory
        structInstanceAdress = createPointerFrom(&testCategory2);
        print("\(structInstanceAdress)  -->  \(testCategory2)")
        
        let testAccount = DemoAccount()
        // DemoAccount类的 实例 的内存地址
        var classInstanceAdress = Unmanaged.passUnretained(testAccount).toOpaque()
        print("\(classInstanceAdress)  --> \(testAccount)")
        
        sleep(1)
        
        testAccount.cDate = Date()
        // DemoAccount类的 实例 的内存地址
        classInstanceAdress = Unmanaged.passUnretained(testAccount).toOpaque()
        print("after 1 secend, modify testAccount.cDate,  testAccount => \(classInstanceAdress)  --> \(testAccount)")
        
        // 引用类型在被赋予到一个变量、常量或者被传递到一个函数时，其值不会被拷贝。因此，使用的是已存在实例的引用，而不是其拷贝
        let testAccount2 = testAccount
        sleep(1)
        testAccount2.cDate = Date()
        print("after 1 secend, modify testAccount2.cDate, testAccount => \(classInstanceAdress)  --> \(testAccount)")

        // 判定两个常量或者变量是否引用同一个类实例
        if testAccount === testAccount2 {
            print("testAccount === testAccount2")
        }
        

        
    }
}
